/*************************************************************************
** Copyright (C) 2014  Mario Marquez <maqzma@gmail.com>.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
*************************************************************************/

#include "Timer.h"

#include <iostream>

using std::cerr;
using std::endl;

Timer::Timer()
    : _running(false)
{
}

Timer::Timer(const Timeout &timeout)
    : _running(false), _timeout(timeout)
{
}

Timer::Timer(const Timer::Timeout &timeout,
             const Timer::Interval &interval,
             bool singleShot)
    : _running(false),
      _isSingleShot(singleShot),
      _interval(interval),
      _timeout(timeout)
{
}

Timer::~Timer()
{
    if (this->running() == true)
        this->stop();
}

void Timer::start()
{
    if (this->running() == true) {
        cerr << "Can not start the timer: ";
        cerr << "Timer is already running!" << endl;
        return;
    }

    _running = true;

    _thread = std::thread(
                &Timer::_temporize, this);
}

void Timer::stop()
{
    if (this->running() == true) {
        _running = false;
        _thread.join();
    }
}

bool Timer::running() const
{
    return _running;
}

void Timer::setSingleShot(bool singleShot)
{
    if (this->running() == true) {
        cerr << "Can not change SingleShot property: ";
        cerr << "The timer is running!" << endl;
        return;
   }

    _isSingleShot = singleShot;
}

bool Timer::isSingleShot() const
{
    return _isSingleShot;
}

void Timer::setInterval(const Timer::Interval &interval)
{
    if (this->running() == true) {
        cerr << "Can not change Interval property: ";
        cerr << "The timer is running!" << endl;
        return;
   }

    _interval = interval;
}

const Timer::Interval &Timer::interval() const
{
    return _interval;
}

void Timer::setTimeout(const Timeout &timeout)
{
    if (this->running() == true) {
        cerr << "Can not change Timeout property: ";
        cerr << "The timer is running!" << endl;
        return;
   }

    _timeout = timeout;
}

const Timer::Timeout &Timer::timeout() const
{
    return _timeout;
}

void Timer::_temporize()
{
    // Ejecutamos un solo ciclo si es singleshot.
    if (_isSingleShot == true) {
        this->_sleepThenTimeout();
    }
    else {
        // Si no es singleshot, ejecutamos hasta
        // que el usuario pare el temporizador.
        while (this->running() == true) {
            this->_sleepThenTimeout();
        }
    }
}

void Timer::_sleepThenTimeout()
{
    std::this_thread::sleep_for(_interval);

    // Aseguramos que el temporizador
    // no se detuvo a medio cilo.
    if (this->running() == true) {
        if (this->timeout() != nullptr)
            this->timeout()();
        else
            cerr << "Can not call Timeout: "
                    << "Timeout is nullptr!" << endl;
    }
}
